home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 1995 November / Macworld Nov ’95.toast / Developers / Selection ƒ 2.5 / About Selection Framework < prev    next >
Encoding:
Text File  |  1994-11-06  |  41.0 KB  |  975 lines  |  [TEXT/MSET]

  1. Note: You need not read this file to begin using and experimenting with
  2. the Selection Framework, although it is recommended that you do so.
  3.  
  4. If you want to quickly get going to see how it all works, then simply do the
  5. following:
  6.  
  7. 1) Make sure you are using Mops version 2.5 or later.
  8.  
  9. 2) Modify your "Mops.paths" file to include the "Selection ƒ 2.5" folder.
  10.  
  11. 3) Place the file "Selection.rsrc" in your "Mops ƒ" folder (this file contains a
  12.    a resource needed by the picture demos).
  13.  
  14. 4) Load the file "Load Std Environment" into Mops.  This a file load script
  15.    that will load all of the needed files for the Selection Framework.
  16.  
  17. 5) Just start browsing the provided files.  At the end of each file will be
  18.    an example of how to use the new classes.  I kind of like the editlist
  19.    class.  You'll catch on pretty quickly.
  20.  
  21.  
  22. THE SELECTION FRAMEWORK
  23.  
  24. By Douglas B. Hoffman
  25. 06Nov94
  26. CompuServe  72310,1743
  27.  
  28. 565 Countryside Lane
  29. Oakland, MI 48363
  30. USA
  31.  
  32. The Macintosh user interface typically presents the user with a window that
  33. contains several "selectable" objects from which the user can choose.  The
  34. user chooses an object by using the mouse to click on it.  The object will
  35. then hilite itself somehow indicating that it is currently selected, and finally 
  36. the user can perform some operation on that object such as sending keystrokes, 
  37. cutting or pasting information via the clipboard, etc.  Also, some
  38. objects, such as controls, may always be "active" and will immediately respond
  39. to a click of the mouse without first having to be hilited.  Additionally,
  40. when the window deactivates certain objects should change their visual appearance 
  41. along with the window (e.g. controls should deactivate).
  42.  
  43. This type of consistent behavior makes things very easy for the user.  Unfortunately, 
  44. creating this behavior makes things difficult for the programmer.
  45. However, as you will soon see, the object oriented approach can greatly simplify 
  46. the programmers task in handling the Macintosh user interface.  With
  47. these files we present a framework, known as the Selection Framework (SF), that
  48. takes care of the details by doing the right things at the right times and in
  49. such a way that your programming work on the interface is minimized.  We would
  50. prefer that you spend your time working on the aspects of your program that
  51. make it unique rather than wrestling with standardized interface issues (and
  52. we assume that you would prefer this also!).
  53.  
  54. The framework presented here is a rather simple one and does not try to do too
  55. much.  It is believed that the most valuable programming frameworks are those
  56. that are easily understandable by other programmers.  Extremely elegant and
  57. complex frameworks may have the potential to do more, but there is usually
  58. also a large investment in learning time required by the programmer in order
  59. to use it.  So, we have attempted to provide a clear, consistent, and yet simple 
  60. framework for window behavior.  By using this framework, Mops programmers
  61. can gain maximum leverage of the work of others.
  62.  
  63.  
  64.  Selection Windows
  65.  
  66. A selection window, or selWindow  is a primitiveWin subclass that maintains a
  67. list of selectable objects.  When the new: message is sent to a selWindow the
  68. selWindow will send a new: message to each selection object in the list
  69. ( selList ).  Also, the window pointer is passed via the stack along with the
  70. new: message.  The reason for this is that many selection objects, such as
  71. controls and text edit fields, require a reference to their owning window.
  72. This is a requirement of the Macintosh toolbox.  We pass the window reference
  73. even to those objects that don't require it, and of course those objects will
  74. simply ignore it.  Additionally, the selWindow will send the draw: message to
  75. each object immediately after new: so the objects have a chance to place their
  76. image on the screen.
  77.  
  78. The list of selectable objects in each selWindow must of course be initially
  79. "loaded" with the appropriate objects.  There are two basic ways to do this.
  80.  
  81. First, we can instantiate public objects and add them to the selWindow's list
  82. simply by passing a reference to the object along with the add: message to
  83. the window.  We must do this for each object to be added to the list.  For example:
  84.  
  85.  
  86.  selWindow aSelWindow
  87.  test: aSelWindow  \ the window must be new before we can add objects
  88.  
  89.  selobject aSelobject1
  90.  selobject aSelobject2
  91.  selobject aSelobject3
  92.  
  93.  aSelobject1 add: aSelWindow
  94.  aSelobject2 add: aSelWindow
  95.  aSelobject3 add: aSelWindow
  96.  
  97.  
  98. Alternatively, we can have our selection objects be instance variables of the
  99. selWindow.  In this case we must redefine the selWindow's new: method to add
  100. references from each selection ivar to the list.  For example:
  101.  
  102.  :class tSelWindow super{ selWindow }
  103.      selobject aSelobject1
  104.      selobject aSelobject2
  105.      selobject aSelobject3
  106.  
  107.  :m new:
  108.      new: super
  109.      aSelobject1 add: super
  110.      aSelobject2 add: super
  111.      aSelobject3 add: super
  112.      ;m
  113.  
  114.  ;class
  115.  
  116. Note that adding ivars to the selList is very similar to adding public objects. 
  117. The differences are that in the window's new: method we must first
  118. call new: super, then add references to the objects.
  119.  
  120. Using ivars as selection objects is slightly more complex than for public objects, 
  121. but we don't think it to be difficult.  The advantage of using ivars
  122. will occur if several windows are desired because each window will automatically 
  123. "instantiate" its own selection objects and add them to the list, rather
  124. than having to explicitly do this for each new window.
  125.  
  126. The selWindow class also keeps track of which object in the selList is currently 
  127. selected by storing a copy of the reference in an ivar currentSel ( a
  128. var).  We again take advantage of late binding by sending messages to the ivar
  129. currentSel without knowing exactly which object is referenced.  But we do know
  130. that any object placed there will properly respond to a standard set of messages. 
  131. Of course it is up to you, the programmer, to assure that all of your
  132. selection objects can handle these messages.  As you will see, it is quite
  133. easy to design objects that can accept these messages, especially if we design
  134. our selection objects as subclasses of the nullSelect class (more on that
  135. later).  The currently selected object will receive special treatment from the
  136. selWindow.  All keystrokes and clipboard actions will apply only to the currently 
  137. selected object.  This behavior is more than likely what you will always 
  138. want.  For example, if your window contains several different text edit
  139. type objects, you would likely prefer that any keystrokes be sent to only one
  140. of those objects at a time, the currently selected object.  Also, the idle:
  141. message will be sent (very frequently, as a result of null events) only to the
  142. currently selected object.  The idle: message will give your currently selected 
  143. object a chance to do things like blink a text edit caret or perhaps
  144. change the shape of the mouse cursor as it moves over the object.  The standard 
  145. set of clipboard messages are , of course, cut:, copy:, paste:, and clear:.
  146.  
  147. All objects in the selList will receive the same messages at certain times, as
  148. is appropriate.  These messages include new:, activate:, deactivate:, draw:,
  149. and release:.  We already discussed the new: message.  The deactivate: message
  150. will be sent to all selection objects every time the window is disabled by the
  151. user (when another window is selected with the mouse or any other means).  The
  152. activate: message will be sent only to all selection objects that always remain 
  153. active every time the window is enabled by the user (when our window is
  154. selected with the mouse or any other means).  The draw: message will be sent to
  155. each selection object whenever the Macintosh system asks the window to draw 
  156. itself.  The release: message will be sent to each selection object when the 
  157. user has asked the window to be closed.  This will allow each object to properly 
  158. dispose of any heap memory that has been allocated to it.  
  159.  
  160. The most interesting behavior of a selWindow occurs when the window receives a
  161. mouse click.  This will generate a content: message to the window, from the
  162. Macintosh and Mops systems, which in turn will lead to invoking the doContent:
  163. method.  The purpose of the doContent: method is to take care of the selection
  164. behavior.  That is, when the user selects with the mouse a different object in
  165. the window, the window will disable the previously selected object, enable the
  166. newly selected object, and make the newly selected object the "current" object, 
  167. if appropriate, so that keystrokes and clipboard actions will
  168. be directed to it.  Some objects do not respond to keystrokes or clipboard actions, 
  169. so our framework treats them slightly differently.  More on that later.
  170.  Also, doContent: has the task of determining which object in the selList was
  171. "hit", if any, and sending the click: message to that object.
  172.  
  173. So we can see that the doContent: method is doing a lot of things and is very
  174. important to the implementation of the Selection Framework.  It will be useful
  175. to examine the content: and doContent: methods of the selWindow class in detail.
  176.  
  177.  :m content:
  178.      active: self
  179.      IF
  180.          doContent: self
  181.      ELSE
  182.          select: self
  183.      THEN ;m
  184.  
  185. As we can see above, when a content: message is received by a selWindow, a
  186. check is made to see if the window is already active.  If not active, then we
  187. merely select the window, which is the default behavior anyway.  If the window
  188. was already active, then we must determine if any of our selection objects
  189. have been "hit" (selected with the mouse), by invoking the window's doContent: 
  190. method.
  191.  
  192.  :m doContent:  { \ next -- }
  193.      start: SelList    \ Must look for a hit on an object in the list.
  194.      BEGIN
  195.          next: SelList
  196.      WHILE
  197.          -> next
  198.          hit?: next
  199.          IF
  200.              \ You hit something. Is it already the current selection?
  201.              next get: currentSel =
  202.              IF
  203.                  \ You hit the current selection, so do its click method.
  204.                  click: next  ( next = currentSel )
  205.              ELSE
  206.                  \ you hit a different object, so...
  207.                  focus?: next IF
  208.                      \ we must change the focus to the next object
  209.                      get: currentSel deactivate: **
  210.                      activate: next
  211.                      draw: next
  212.                      next put: currentSel
  213.                      ELSE
  214.                          \ don't change the focus, just do a click:
  215.                          click: next
  216.                      THEN
  217.              THEN
  218.          exit  \ must stop this method here
  219.          THEN
  220.      REPEAT
  221.      ;m
  222.  
  223. We can see that the method above uses a BEGIN/WHILE/REPEAT loop to step
  224. through each object in the selList of the window, starting with the first object. 
  225. Actually, the loop will stop at the first hit, so we might not step
  226. through every object.  We step through each object in the selList by using the
  227. start: and next: methods, where start: will cause the ensuing use of next: to return 
  228. the first object in the list.  Subsequent calls to next: will return the
  229. next objects in the list, in order.  Note that the next: method will also return 
  230. a boolean which indicates if we reached the end of the list (false = end
  231. of list).  So our BEGIN/WHILE/REPEAT loop is terminated when next: returns
  232. false (end of list).
  233.  
  234. The WHILE portion of our loop will first check to see if the mouse "hit" that
  235. particular object in the list, and if it did then send the object a click: message 
  236. and/or activate: message as appropriate.  Also, the current selection
  237. ivar is maintained as appropriate, and the last selected object may receive a
  238. deactivate: message.  That's it!  We hope that the Mops programmer wishing to
  239. use the SF will study this behavior so you know what is happening.  Modifications
  240. to this behavior should not be difficult.
  241.  
  242.  
  243.  Selection Objects
  244.  
  245. Objects to be used as selection objects must adhere to a specific behavior
  246. when certain messages are received.  We use the class nullSelect as a way to
  247. ensure that all selection classes subclassed from nullSelect contain all of
  248. the required selection methods.  Below is the definition of the nullSelect
  249. class, from which we can subclass a selection object.  Lets look at each required 
  250. message response in detail to gain a better understanding of the SF.
  251.  
  252. new:  must always expect a window pointer (wptr) on the stack, whether or not
  253. our selection object requires it.  In nullSelect we simply drop it, but if
  254. your object requires the wptr, as many Mac toolbox constructs do (e.g. text
  255. edit and controls), then you should override the new: method and use the wptr
  256. as appropriate.
  257.  
  258. hit?: is perhaps the most important selection method to understand.  Your selection 
  259. object must respond to a hit?: message by testing for whether the
  260. user has clicked on your object or not.  Also, if the user has clicked on your
  261. object, then you must also indicate if the focus should be given to your object. 
  262. The focus?: method will tell if a particular object should receive the
  263. focus by being placed in the currentSel ivar.  If we hit an object and that
  264. object demands the focus, then the framework will send the click: message to
  265. the object and make it the current selection.  The current selection will be
  266. the focus of any keystrokes or clipboard commands as sent by the user.  By
  267. looking at the idle:, key:, cut:, copy:, paste:, and clear: methods for class
  268. selWindow we can see how the current selection is always passed these messages, 
  269. late bound, by the window when the window receives the same message from
  270. the system.  See below.
  271.  
  272.  :CLASS selWindow  super{ primitiveWin }
  273.      var currentSel    \ the currently selected object; could be a nullSelect
  274.      10 List SelList    \ a list of objects that can be selected
  275.  
  276.  ...
  277.  
  278.  :m idle:
  279.      get: currentSel idle: ** ;m
  280.  
  281.  :m key: ( char -- )
  282.      get: currentSel key: ** ;m
  283.  
  284.  :m cut:
  285.      get: currentSel cut: ** ;m
  286.  
  287.  :m copy:
  288.      get: currentSel copy: ** ;m
  289.  
  290.  :m paste:
  291.      get: currentSel paste: ** ;m
  292.  
  293.  :m clear:
  294.      get: currentSel clear: ** ;m
  295.  
  296.  ...
  297.  
  298. So all of our selection objects must also be able to handle these six focus
  299. messages.  Of course we can always choose to have our objects "ignore" these
  300. messages by simply doing nothing.  But these messages must still be present in
  301. the class definition.
  302.  
  303.  
  304. When our selection window is sent the new: message, it will in turn send the
  305. new: message to each selection object in the selList.  The selection window
  306. will also pass, on the stack, its own window pointer in case the selection object 
  307. requires that (e.g. controls, text edit objects, List Manager objects, etc.).
  308.  
  309.  
  310.  :CLASS nullSelect super{ object }
  311.  
  312.  \ we always pass the window pointer, whether it is needed or not, for
  313.  consistency
  314.  :m new: ( wptr -- ) drop ;m
  315.  :m release: ;m
  316.  
  317.  :m activate: ;m
  318.  :m deactivate: ;m
  319.  
  320.  
  321.  :m idle: initCursor ;m    
  322.  :m draw: ;m
  323.  :m key: ( char -- ) drop ;m
  324.  
  325.  :m hit?: ( -- f )  false ;m
  326.  :m click: ;m
  327.  :m focus?: ( -- f )  false ;m
  328.  :m alwaysActive?: ( -- t ) true ;m
  329.  
  330.  :m cut: ;m
  331.  :m copy: ;m
  332.  :m paste: ;m
  333.  :m clear: ;m
  334.  
  335.  ;CLASS
  336.  
  337.  
  338.  
  339.  
  340.  
  341.  Using Selection Objects
  342.  
  343.  
  344.  PREDEFINED SELECTION CLASSES
  345.  
  346. In the following section the various standard selection classes are described.  
  347. Each and every method for the classes are not described because that is best 
  348. learned by inspecting the actual source code.  Also, examples of how to use each 
  349. class are provided at the end of each source code listing.  
  350.  
  351. The selection classes were designed to make your work in programming the Macintosh 
  352. user interface much easier.  As such, implementing any of the selection 
  353. classes is extremely easy and no matter which selection class you use the
  354. way you use it is consistent for all selection classes.
  355.  
  356.  StaticText
  357.  
  358. StaticText, or non-editable text in the sense that it is not TextEdit text, is
  359. text that is meant for display.  StaticText remembers everything it needs to
  360. draw itself including the position in the window, the font, fontsize, etc.  A
  361. maximum of 16 characters will fit in a StaticText object.
  362.  
  363.  Note the heavy reliance on multiple inheritance.
  364.  
  365.  
  366.  PushButton and checkBox+
  367.  
  368. A pushButton and checkBox+ will function as is without any required setup.
  369. They have default titles and positions, and will respond appropriately to
  370. mouse clicks.  Action handlers must be installed in order for these controls
  371. to actually do anything.
  372.  
  373.  
  374.  RadioGroup
  375.  
  376. A radioGroup is an array of radio buttons, we normally would never use a radio
  377. button except as part of a group.  The buttons are vertically aligned and
  378. behave as expected.  We optionally use init: to set the initial position and
  379. which button will be the first to be set.  We use get: to obtain which button
  380. is now on, get: returns the zero-based index of the currently selected button.
  381.  
  382.  
  383.  VscrollBar and Hscrollbar
  384.  
  385. The scrollbars are fully functional and will respond to mouse clicks with no
  386. additional setup required.  Scrolling is accomplished by sending late bound
  387. messages to a second object, the object to be scrolled. The default object to
  388. be scrolled is nullOwnerObject which accepts the required messages from the
  389. scrollbar prescroll:, postscroll:, and draw:.  We can have the scrollbar
  390. control any other object by using the scrolledby: method.  Of course we must
  391. define methods that behave properly to the scrollbar messages.
  392.  
  393. See the use of scrollbars in classes tescroll, editlist, and pictscroll.
  394.  
  395. Note that the number of pixels to scroll and the max and min control values
  396. must be set up as well.  Default values are provided that are reasonable.
  397.  Perhaps we should have a method that inspects the object to be scrolled for
  398. those values?  Also, the rectangle to scroll must be set up via
  399. setscrollrect:.
  400.  
  401.  
  402.  Text Edit Classes
  403.  
  404. Class terec is strictly intended for use as a way to conveniently access the
  405. Mac toolbox data structure for a TextEdit record.  As can be seen in class te,
  406. we don't instantiate a terec object or ivar.  Instead, we obtain a pointer to
  407. the record ( ptr: TEHandle, in this case) and then pass a message to the class
  408. itself!
  409.  
  410. Note that we normally would subclass from te, as we do in tebox and tescroll.
  411. A te is a selection object and perhaps is one of the best examples of the  advantages 
  412. of using the selection framework.  We can have as many te objects as
  413. we wish in a window and they will all behave as expected in that each must
  414. first be selected with the mouse and then keystrokes may be received.  All we
  415. need do is instantiate as many te objects as we wish and then add: them to a
  416. selwindow.  Of course we should at least reposition the te objects (use move:
  417. or moveto:) so that they do not all lay on top of each other.
  418.  
  419. Also note that the clipboard is supported automatically.
  420.  
  421. A tebox is simply a te with a black border.
  422.  
  423. A tescrollV is a textbox with a vertical scrollbar so we can scroll through
  424. multi-line text in the standard Macintosh manner.
  425.  
  426.  
  427.  List Manager
  428.  
  429. The ListManager class is intended for subclassing.  A 1 column list, a list-col
  430. is based on the ListManager.  We treat the list as if it were a 1-
  431. dimensional array and use the access methods to: and at: in the expected
  432. fashion, with the address and length of the string being the passed data.
  433.  
  434.  Edit List
  435.  
  436. An editlist is a scrollable/editable row and column matrix of text data.
  437. While resembling our ListManager class behavior, note that we do *not* use any
  438. ListManager routines at all.  Of course we follow our selection protocol so
  439. use is very simple and we can have multiple editlists in a window.  One unique
  440. feature of an editlist is that the TextEdit field will appear *in* the current
  441. cell being edited.
  442.  
  443. Note that classinit: contains all of the setup parameters (number of rows and
  444. columns and so forth).  Default is 30 rows and 30 columns, but we don't
  445. display all at once.  We can scroll through them.  A subclass could easily
  446. have different values.
  447.  
  448. Note also that we provide for a filterprocedure: method that could be over
  449. ridden to inspect data from a cell after it is entered, or any time we attempt
  450. to leave that cell.  Here the filterprocedure: method simply returns true.
  451. Returning false would result in the user being returned to the offending cell
  452. until acceptable data was entered.
  453.  
  454. Like our TextEdit objects, we can have multiple editlists, or editlists and
  455. TextEdit objects in the same window and the standard Macintosh behavior will
  456. automatically occur.
  457.  
  458.  
  459.  Popup Menu
  460.  
  461. Class popup allows us to easily have popup menus in a selwindow by following
  462. our standard selection protocol.  Popups are subclassed from the standard Mops
  463. menu class so storing xts for the popup to execute is just like for standard
  464. menus.  One unique feature of the popup class is we need not specify a menu ID
  465. because the new: method will simply choose a unique ID for us, we don't care
  466. which.
  467.  
  468. Also, in keeping with our philosophy that a minimum, preferably no, setup
  469. should be required to use our objects, class popup will function quite nicely
  470. simply by instantiating and adding to our selwindow.
  471.  
  472.  
  473.  Picture Classes
  474.  
  475.  
  476.  
  477.  
  478.  DESIGNING YOUR OWN SELECTION CLASSES
  479.  
  480. Perhaps two of the most difficult concepts presented here are designing 
  481. a selection object and designing an object that implements scrolling.  So
  482. the following will provide some insight into a process that might be used for
  483. designing both.
  484.  
  485. Suppose we wish to have a selection object that will allow us to view a picture. 
  486. In addition, we wish to be able to view pictures that are larger than
  487. the view area that we have allowed in our window.  Clearly, that means we need
  488. to use something like scrollbar controls.  We wish the use of our new class of
  489. objects to fit the standard protocol that we have defined, so the use of an
  490. object of the class pictScroll would be as follows:
  491.  
  492.  pictScroll p
  493.  
  494.  selwindow w
  495.  p add: w
  496.  test: w
  497.  
  498. When we execute test: w we want a fully functional selection object to appear
  499. in a window, ready for action.  All necessary behavior will occur automatically 
  500. (calling new: at the right time, drawing at the right time, releasing memory 
  501. at the right time, etc.)  So, following our general strategy for all objects, 
  502. the default behavior for a pictScroll object will be to load itself
  503. with an available test pict resource and use reasonable values for positioning
  504. itself and displaying itself in a window.  Naturally, we can always change the
  505. resource ID and size and positioning if we choose.
  506.  
  507. The first decision we make whenever we design a new class is which superclass(s) 
  508. to use.  A scrolling picture seems to be a lot like a picture, so we
  509. decide to start with the picture class as superclass.  This is very convenient
  510. because we notice that the picture class already inherits from both the nullSelect 
  511. class and the ownerobj class.  So we know that any objects we instantiate 
  512. can already be used as both a selection object and a scrollbar owner object 
  513. without defining any more methods!  The advantage to doing it this
  514. way is if we forget to define one of the required methods then our program
  515. will still run because all protocol messages would be recognized by our object
  516. (but of course there will be no appropriate response for meaningful behavior), 
  517. instead of having Mops stop everything and reporting an error.  Sometimes 
  518. we could even crash and need to restart our computer.  This would be
  519. very disruptive to our creative programming process.
  520.  
  521. The next decision we make is what instance variables should be used?  Obviously, 
  522. we need two scrollbar controls.  Additionally, we decide it would look
  523. nice to frame the rectangle in which we view the picture with a black border. 
  524. So we can now begin to define our pictScroll class as follows:
  525.  
  526.  :class pictScroll super{ picture }
  527.      vscrollbar VScrollControl
  528.      hscrollbar HScrollControl
  529.      graphicRect frame
  530.  
  531.  ;class
  532.  
  533.  pictScroll p
  534.  
  535.  
  536. Now we can instantiate a pictscroll object, p, and add: it to a selwindow.
  537. Test: ing the selwindow will result in no errors because our new selection object 
  538. will respond to every required message.  Of course p will not do any
  539. scrolling yet, nor can we even see the scrollbars on the screen because we
  540. have not redefined any of the superclass methods.  But we can verify at this
  541. time that we have a valid pict resource that will properly show up on the
  542. screen.  So for now our pictScroll object behaves exactly like a picture object.
  543.  
  544. Next we decide that we should attempt to get the scrollbars and the frame to
  545. draw: themselves.  So we define a draw: method for class pictScroll, overriding 
  546. the superclass draw:.  We also recognize that we must send the new: message 
  547. to our scrollbar ivars before we can draw: them, so we must also define a
  548. new: method.  We are careful to observe the convention that all new: methods
  549. for selection objects will require the window pointer (wptr) on the stack at
  550. new: time.  We are also very careful to call new: super so all of the pict
  551. new: action will occur.
  552.  
  553.  :m new: { wptr -- }
  554.      wptr new: super
  555.      wptr new: VScrollControl
  556.      wptr new: HScrollControl
  557.  ;m
  558.  
  559. We remember that we should redefine release: so that the memory for the
  560. scrollbars is properly returned when we close the test window.  Again, we remember 
  561. to call the superclass release: method (in this case, we return memory
  562. occupied by the PICT resource).
  563.  
  564.  :m release:
  565.      release: super
  566.      release: VScrollControl
  567.      release: HScrollControl
  568.  ;m
  569.  
  570.  
  571. At this time it dawns on us that we should define a default reasonable rectangle 
  572. for our picture to be viewed in.  So we use classinit: to set up our
  573. graphicRect ivar (frame).
  574.  
  575.  :m classinit:
  576.      50 50 150 150 put: frame
  577.      ;m
  578.  
  579.  
  580. Now we feel we are ready to define our draw: method, whose task is solely to
  581. place the image of our object on the screen.  We call draw: super so the
  582. picture will draw itself (our superclass is picture, remember).
  583.  
  584.  :m draw:
  585.      draw: super
  586.      draw: frame
  587.      draw: VScrollControl
  588.      draw: HScrollControl
  589.      ;m
  590.  
  591.  
  592. Again, we instantiate our object, p, and test it in a window.  We are pleased
  593. to see that now both of our scrollbars appear and our frame appears.  We are
  594. slightly dismayed, but should not be surprised, that everything is out of position 
  595. and our scrollbars do not respond to mouse clicks.  But that's ok, we
  596. actually have quite a bit of code up and running already.  Our code is properly 
  597. making all of the necessary toolbox calls to create, display, and destroy
  598. pictures and scrollbars in a window of our choice.  That code is doing a lot
  599. already, and was not very difficult to create!  We continue on, somewhat
  600. inspired at this point.
  601.  
  602. So our next task is to properly position the objects.  We decide to create a
  603. new method just for doing this, and call it position:.  We notice that we can
  604. use position: in our classinit: method.
  605.  
  606.  :m position:
  607.      get: frame { l t r b -- }
  608.      r 1- ( x) t ( y) b t - ( len) init: VScrollControl
  609.      l ( x) b 1- ( y) r l - ( len) init: HScrollControl
  610.      l t moveto: super
  611.  ;m
  612.      
  613.  :m classinit:
  614.      classinit: super
  615.      50 50 150 150 put: frame
  616.      position: self
  617.      ;m
  618.  
  619. Making these changes, we see that everything looks to be positioned properly on the 
  620. screen (Actually, we probably have used a little trial and error because it is easy 
  621. to be off by 1 pixel here or there.  Forth is so interactive and it is so easy and 
  622. fast to make changes that there is little need for an "interface drawing" tool).  
  623.  
  624. Wait a minute!  We forgot to clip the drawing of the picture to the frame.  No
  625. problem, simply call ClipRect with appropriate values just before and just
  626. after draw: ing  the picture.  So we change our draw: method as follows:
  627.  
  628.  
  629.  :m draw:
  630.      get: frame put: temprect  1 1 inset: temprect  temprect call ClipRect
  631.      draw: super
  632.      0 0 32000 32000 put: temprect  temprect call ClipRect \ no clipping
  633.      draw: frame
  634.      draw: VScrollControl
  635.      draw: HScrollControl
  636.      ;m
  637.  
  638. A quick check verifies that we are now properly clipping the picture drawing.
  639.  
  640. So now we are ready to define the methods that allow our object to respond to
  641. user input, in this case mouse input.  We start by defining our hit?: and
  642. click: methods.  Hit?: tests to see if the mouse was clicked on our object, if
  643. it was then the selWindow will send a click: method our selobject which in
  644. turn must send a click: message to the appropriate scroll control.  Note that
  645. we will only test to see if the mouse hit one of our two controls.  We decide
  646. that a hit to the picture itself (the frame rectangle) has no meaning for this
  647. type of object.
  648.  
  649.  :m hit?:  ( -- f )
  650.      hit?: VScrollControl
  651.      hit?: HScrollControl  or
  652.  ;m
  653.  
  654.  :m click:
  655.      hit?: VScrollControl IF click: VScrollControl THEN
  656.      hit?: HScrollControl IF click: HScrollControl THEN
  657.  ;m
  658.  
  659.  
  660. Note we are having to retest for which control was hit in our click: method.
  661. We could save a little code and a little time by incorporating the click: message 
  662. sending in our hit?: method, but we choose to keep our code consistent
  663. and understandable.  Besides, Mops is plenty fast and compact enough to allow us to be
  664. slightly redundant.
  665.  
  666. Testing these two new methods shows that we do indeed cause our scrollbars to
  667. respond to the mouse.  But the picture does not yet scroll!  
  668.  
  669. So, referring to the recipe for scrolling, we see that, at a minimum, we must
  670. do two things:  1) tell the scrollbar(s) what ownerObject to use, and 2) tell
  671. the scrollbar(s) the rectangle that should be scrolled.  Setting the ownerObject 
  672. must be done every time we run our program, so we must do this in our
  673. new: method and not our classinit: method.  This is a very important point to
  674. remember.  The reason is because the ownerObject reference is actually an address. 
  675. Since all addresses can, and usually do, change every time we run a
  676. program (one never knows where in the Mac's memory a program will be loaded),
  677. we must "reset" this address every time.  The ownerObject is stored in the
  678. scroll control so the scroll control can send certain messages to it at just
  679. the right time.  This approach to scrolling has the advantage of decoupling
  680. the scrollbar code from our other code (in this case the pictScroll code).  So
  681. we need not define custom scroll controls that will only work with certain
  682. code.
  683.  
  684. Redefining our pictScroll new: method to include putOwnerObject: goes like
  685. this:
  686.  
  687.  :m new: { wptr -- }
  688.      ... as before ...
  689.  
  690.      self putOwnerObject: VScrollControl
  691.      self putOwnerObject: HScrollControl
  692.  ;m
  693.  
  694. Setting the rectangles to scroll can be done anytime, not just when new, so we
  695. choose a convenient place to do it, here the position: method seems convenient. 
  696. Also, we set the same rectangle values for both scrollbars, which is
  697. appropriate for a pictscroll type object.
  698.  
  699.  :m position:
  700.      ... as before ...
  701.  
  702.      l t r b put: temprect 1 1 inset: temprect
  703.      get: temprect setScrollRect: VScrollControl
  704.      get: temprect setScrollRect: HScrollControl
  705.  ;m
  706.  
  707. Retesting our code shows that something like what we want for scrolling the
  708. picture definitely happens, but it isn't quite right.  The picture looks like
  709. it is trying to scroll, but when we release the mouse the picture appears to
  710. not have scrolled at all!  It is only now that we remember we must do one more
  711. thing...  We must redefine our draw: method to always move the picture relative 
  712. to the scrollbar control values.  So  our new draw: method looks like
  713. this:
  714.  
  715.  :m draw:
  716.      get: frame { l t r b -- }
  717.      l get: HScrollControl -  ( x)
  718.      t get: VScrollControl -  ( y)  moveTo: super    \ picture moveto: method
  719.  
  720.      ... as before ...
  721.      ;m
  722.  
  723. Retesting our code shows that we really can now scroll our picture!  The only
  724. "fine tuning" remaining to be done is to set the range for our scrollbars so
  725. our picture will scroll all the way to its edges, and not scroll "too far".
  726. Since we are using pixel based scrolling, we should set the scroll controls'
  727. ranges to a low of zero and a high corresponding to the width or height of the
  728. picture.  We can set these values in our position: method.
  729.  
  730.  :m position:
  731.      ... as before ...
  732.  
  733.      0 ( lo) width: super width: frame - 5 + ( hi)    putrange: HScrollControl
  734.      0 ( lo) height: super height: frame - 5 + ( hi)    putrange: VScrollControl
  735.  ;m
  736.  
  737. Note that we could also change the number of pixels to scroll by using the
  738. scroll control setScrollValues: method.  But the default values seem to work
  739. just fine for us here.
  740.  
  741. Also, we should define activate: and deactivate: methods so our pictscroll objects 
  742. will properly activate: and deactivate: upon window enable: and disable:.
  743.  
  744.  
  745.  :m activate:
  746.      activate: VScrollControl
  747.      activate: HScrollControl ;m
  748.  
  749.  :m deactivate:
  750.      deactivate: VScrollControl
  751.      deactivate: HScrollControl ;m
  752.  
  753.  
  754.  
  755.  OTHER CLASSES and SUPPORT WORDS
  756.  
  757. In this section we describe the many support classes and words for the Selection 
  758. Framework that are included in this specially extended version of Mops.
  759.  In several instances the classes are subclasses of existing standard Mops
  760. classes (e.g. point+ and rect+) that do not alter existing method behavior but
  761. add additional required methods.  As such, it would be possible to simply redefine 
  762. the original classes.  This would save a little dictionary space.  But
  763. we leave these as separate classes in order to clearly distinguish what is
  764. different with these Mops extensions.
  765.  
  766.  File DBH Mops Extensions
  767.  
  768.  mDrawString  ( addr len -- )
  769.  mStringWidth    ( addr len -- width )
  770.  
  771. The Macintosh Toolbox traps DrawString and StringWidth are very useful and
  772. used extensively in our version of Mops.  Both traps require a $ptr (string
  773. pointer, or Str255 data type).  In keeping with the Mops convention of always
  774. using strings in the  address length form, we define the Mops word equivalents
  775. of the toolbox traps by appending an "m" to the front of each name ( m stands
  776. for Mops DrawString and Mops StringWidth).  We could have, of course, simply
  777. named the words the same as the toolbox traps but this would be confusing to
  778. the programmer who is familiar with the trap as described in Inside Macintosh.
  779.  
  780.  number>$  ( n -- addr len)
  781.  
  782.  Number-to-string is used to convert any non floating point number on the stack
  783.  to a string.  Negative numbers will have a minus sign appended to the front of
  784.  the string.
  785.  
  786.  
  787.  >heap and >dispose
  788.  
  789. >heap and >dispose allow us to dynamically create nameless objects from the
  790. heap in the same way Neon did.  The advantage to using this method over, for
  791. example OBJHANDLE, is that we can simply send messages straight to the object
  792. if we store the returned pointer in a value or local variable (we need not
  793. first send the obj: method first).  Of course since we are locking a handle we
  794. should be careful not too create too many of these objects such that the heap
  795. is fragmented.  This should not be a problem for relatively small or transient
  796. objects created with >heap.
  797.  
  798.  The Clipboard
  799.  
  800. Clipboard menu handlers for our standard selection  environment are provided
  801. in the file named Clipstuff.  We only implement text data, but of course we
  802. could generalize to pict data if we wish.  We define the words selcut, selcopy, 
  803. selpaste, and selclear which simply send late-bound messages to the
  804. currently active Mops window.
  805.  
  806.  Event+
  807.  
  808. In class event+ we provide a few new methods for class event.  We should probably 
  809. just revise our event class to contain these methods.  Try this from the
  810. Mops front end window with and without the shift key 
  811. pressed:   shiftKey?: fevent .  
  812.  
  813. We also provide the methods commandKey?: capslockKey?:  and optionKey?: which
  814. behave in an analogous manner to shiftKey?: in that we simply pass the corresponding 
  815. message to object fevent in order to determine the state of the Macintosh 
  816. modifier keys.
  817.  
  818.  Also note that we actually changed the class of object fevent.
  819.  
  820.  
  821.  Class ptrlist
  822.  
  823.  We will use a ptrlist as an ivar in class selwindow to dynamically allocate
  824.  pointers to objects that we add: to the windows list of objects.
  825.  
  826.  Class nullselect
  827.  
  828.  A nullSelect object will accept all of the standard protocol messages for the
  829.  currentSel in a SelWindow. If we always subclass from nullselect then we are
  830.  assured of not forgetting to define one of the required messages, even if we
  831.  don't use it.
  832.  
  833.  Class primitiveWin
  834.  
  835.  A primitive window class with no frills.  Won't work as a console.  Note that
  836.  this class is redundant with class window.  We should be able to replace class
  837.  window with this as long as we *never* try to use fwind.  Class primitiveWin
  838.  is used a the superclass for class selwindow.
  839.  
  840.  
  841.  
  842.  $x
  843.  
  844.  Class $x is a dictionary-based simple string class whose length may vary, up
  845.  to a maximum of 255, but the maximum length is defined at instantiation.  We
  846.  cheat a bit here and use Mops' INDEXED class definition abilities and indexed
  847.  ivar data area in a way that was not really intended.  
  848.  
  849.  $x's are nice for use as string ivars, or if you want a persistent string object 
  850. in the dictionary (no handles here so we don't need to do a new: and
  851.  restore the data at each runtime).
  852.  
  853.  In class $x we take advantage of the fact that the 2-byte Width field for indexed 
  854. objects can be used for other storage *if* we are careful. Since the
  855.  Width field is only really needed here at instantiation (we are careful not to
  856.  use words that rely on Width actually being the width), we use it here to
  857.  store the maximum length, or limit, of text in the $x.  Also, we now use the
  858.  byte just prior to the indexed data area to store the length of the text, so
  859.  it is easy to obtain a str255 format string since all we need do is obtain
  860.  this address (which is idxbase - 1 , see get$: ).
  861.  
  862.  Note that there are still 4 unused bytes that might be used for pos and lim as
  863.  in string.  I guess we are safe doing this until Michael changes the internal
  864.  structure of indexed objects. (!!)
  865.  
  866.  
  867.  Rect+
  868.  A rect+ is simply a rect that initializes its data to something other
  869.  than zero.  We also add some additional methods.
  870.  
  871.  
  872.  
  873.  Multiple Inheritance
  874.  Multiple inheritance allows us to declare more than one superclass for any new
  875.  class that we are defining.  The new class will then inherit data and methods
  876.  from its superclasses!  We must be very careful to understand exactly which
  877.  
  878.  Now why, you may wonder, would we want to do this?  The simple answer is because 
  879. we are lazy and we want the programming system to do as much work as
  880.  possible for us.  Additionally, we actually save room in the dictionary (i.e.
  881.  our program).  Perhaps just as important as saving programming effort and program 
  882. size, multiple inheritance is a very "natural" way for us to think about
  883.  our objects.  To make a biological analogy, a child may have inherited blue
  884.  eyes from his mother and black hair from his father.  To carry the analogy a
  885.  bit further, we also would not expect the child to inherit both blue eyes and
  886.  brown eyes (if the father had brown eyes), but only blue or only brown.  So it
  887.  is with multiple inheritance.  When the same data exists in two different superclasses, 
  888. only one of the superclass's data will be used.  We won't carry
  889.  the biological analogy any further because it likely will not hold (for example, 
  890. we do not get "mixtures" of properties with programming multiple inheritance 
  891. as we might get with some biological multiple inheritance).  But, the
  892.  point here is that multiple inheritance can be a very intuitive way to think
  893.  about creating objects to solve problems.
  894.  
  895.  Multiple inheritance was once described as a very advanced feature that was
  896.  only available in certain esoteric programming systems.  Well, that may still
  897.  be partially a true statement.  But Mops provides a very easy to use multiple
  898.  inheritance mechanism.
  899.  
  900.  So how does multiple inheritance save us work?  By using multiple inheritance,
  901.  we can easily create a new class whose objects perform just as we wish.  By
  902.  easy, we mean that we do not, in general, have to explicitly define every
  903.  method.  Often, we can define a new class with multiple inheritance simply by
  904.  declaring the superclasses.  Very little message defining will then be required.
  905.  
  906.  Consider an example.  Let's say we wish to create a class of text that has the
  907.  ability to display itself in a rectangle.  We would like the coordinates of
  908.  the rectangle to be part of the object and we would like to be able to move
  909.  the rectangle around with move: and moveto: messages and also we would like
  910.  all of the other rectangle messages to apply.  But at the same time we wish to
  911.  be able to use our string methods, such as new: and print: and so forth.
  912.   Without multiple inheritance, we might singly inherit from one class, say the
  913.  string class, but then we must create an ivar for the other class, in this
  914.  case a rectangle class.
  915.  
  916.  :class stringRect super{ string+ }
  917.      rect+ theRect
  918.  
  919.  :m classinit:
  920.      50 50 100 65 put: theRect ;m
  921.  
  922.  :m draw:
  923.      addr: theRect
  924.      draw: super ;m  \ will use draw: method of class string+ 
  925.  
  926.  ;class
  927.  
  928.  So in our stringRect class defined above with single inheritance we have a
  929.  class that will work.  Since string+ is the superclass we automatically have
  930.  all of the string+ methods available to any stringRect object without doing
  931.  any more work.  But, if we wish to use any of the rect+ methods, such as move:
  932.  and moveto:, then we have some more programming work to do.  Below we will add
  933.  the move: and moveto methods to our stringRect class:
  934.  
  935.  :class stringRect super{ string+ }
  936.      rect+ theRect
  937.  
  938.  :m classinit:
  939.      50 50 100 65 put: theRect ;m
  940.  
  941.  :m draw:
  942.      addr: theRect
  943.      draw: super ;m  \ will use draw: method of class string+
  944.  
  945.  :m move:    ( dx dy -- )
  946.      move: theRect ;m 
  947.  
  948.  :m moveto:    ( x y -- )
  949.      moveto: theRect ;m 
  950.  
  951.  ;class
  952.  
  953. Actually, that wasn't too bad, thanks to Mops's ability to send messages to
  954. ivars.  But what about all of the other rect+ methods we would like to have
  955. available?  Since ivar methods are not publicly available, we must write a
  956. similar "pass-through" method for each and every rect+ method we wish our
  957. stringRect objects to have!  Ouch!  Not difficult, but certainly very tedious.
  958. And also prone to typing errors.  There must be a better way and of course there
  959. is, multiple inheritance!
  960.  
  961.  Now let's redefine our example with multiple inheritance:
  962.  
  963.  :class stringRect super{ string+ rect+ }
  964.  
  965.  :m classinit:
  966.      50 50 100 65 put: super> rect+
  967.  ;m
  968.  
  969.  :m draw:
  970.      addr: super> rect+
  971.      1 ( justification)
  972.      draw: super ;m  \ will use draw: method of class string+ 
  973.  
  974.  ;class
  975.